﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XNAViewerSample.Physics
{
    public interface ForceGenerators
    {
        void ApplyForce(SoftBodyObjects sbObject);
    }

    public sealed class Gravity : ForceGenerators
    {
        #region Fields
        private Vector3 acceleration;

        public Vector3 Acceleration
        {
            get { return acceleration; }
            set { acceleration = value; }
        }
        /*
        public float AccelerationX
        {
            get { return acceleration.X; }
            set { acceleration.X = value; }
        }

        public float AccelerationY
        {
            get { return acceleration.Y; }
            set { acceleration.Y = value; }
        }

        public float AccelerationZ
        {
            get { return acceleration.Z; }
            set { acceleration.Z = value; }
        }
         */
        #endregion

        #region Constructor
        public Gravity()
            : this(new Vector3(0, -9.81f, 0)) { }

        public Gravity(Vector3 acceleration)
            : base()
        {
            this.acceleration = acceleration;
        }
        #endregion

        #region Methods
        public void ApplyForce(SoftBodyObjects sbObject)
        {
            sbObject.ResultantForce += sbObject.Mass * acceleration;
        }
        #endregion
    }


    public sealed class Spring : ForceGenerators
    {
        #region Fields
        private float stiffness;
        private float damping;
        private float restLength;
        private SoftBodyObjects sbObjectA;
        private SoftBodyObjects sbObjectB;

        public float Stiffness
        {
            get { return stiffness; }
            set { stiffness = value; }
        }

        public float Damping
        {
            get { return damping; }
            set { damping = value; }
        }

        public SoftBodyObjects SbObjectA
        {
            get { return sbObjectA; }
            set { sbObjectA = value; }
        }

        public SoftBodyObjects SbObjectB
        {
            get { return sbObjectB; }
            set { sbObjectB = value; }
        }
        #endregion

        #region Constructor
        public Spring(float stiffness, float damping, SoftBodyObjects sbObjectA, SoftBodyObjects sbObjectB)
            : this(stiffness, damping, sbObjectA, sbObjectB, (sbObjectA.CurrPosition - sbObjectB.CurrPosition).Length()) { }

        public Spring(float stiffness, float damping, SoftBodyObjects sbObjectA, SoftBodyObjects sbObjectB, float restLength)
            : base()
        {
            this.stiffness = stiffness;
            this.damping = damping;
            this.sbObjectA = sbObjectA;
            this.sbObjectB = sbObjectB;
            this.restLength = restLength;
        }
        #endregion

        #region Methods
        private Vector3 direction;
        private float currLength;
        private Vector3 force;
        public void ApplyForce(SoftBodyObjects sbObject)
        {
            //get the direction vector
            direction = sbObjectA.CurrPosition - sbObjectB.CurrPosition;

            //check for zero vector
            if (direction != Vector3.Zero)
            {
                //get length
                currLength = direction.Length();

                //normalize
                direction.Normalize();

                //add spring force
                force = -stiffness * ((currLength - restLength) * direction);

                //add spring damping force
                //force += -damping * Vector3.Dot(sbObjectA.CurrVelocity - sbObjectB.CurrVelocity, direction) * direction;
                force += -damping * (sbObjectA.CurrVelocity - sbObjectB.CurrVelocity);

                //apply the equal and opposite forces to the objects
                sbObjectA.ResultantForce += force;
                sbObjectB.ResultantForce += -force;
            }
        }
        #endregion
    }

    public sealed class Medium : ForceGenerators
    {
        private float dragCoefficient;

        public float DragCoefficient
        {
            get { return dragCoefficient; }
            set { dragCoefficient = value; }
        }

        //--------------------------------------------------------

        public Medium(float dragCoefficient)
            : base()
        {
            this.dragCoefficient = dragCoefficient;
        }

        public void ApplyForce(SoftBodyObjects sbObject)
        {
            sbObject.ResultantForce += -dragCoefficient * sbObject.CurrVelocity;
        }
    }
}
